
package xj.graph2d.tree;

import static xj.app.BaseApplicationFrame.getResourceString;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;

import javax.swing.*;
import javax.swing.tree.*;

import xj.app.BaseApplicationFrame;
import xj.app.LookAndFeelManager;
import xj.graph2d.Document;
import xj.graph2d.DocumentException;
import xj.graph2d.SimpleDrawDocument;
import xj.graph2d.app.DrawApp;
import xj.graph2d.app.project.BreadcrumbAdapter;
import xj.graph2d.app.project.EditProjectItemListener;
import xj.graph2d.app.project.Project;
import xj.graph2d.app.project.ProjectException;
import xj.graph2d.app.project.ProjectFolder;
import xj.graph2d.app.project.ProjectFolderPropertiesListener;
import xj.graph2d.app.project.ProjectItem;
import xj.graph2d.app.project.ProjectItemPropertiesListener;
import xj.graph2d.app.project.ProjectListener;
import xj.graph2d.app.project.ProjectManager;

public class ProjectPane 
  extends JPanel 
  implements ProjectListener {

  public ProjectPane(DrawApp main, Map<String, Icon> iconMap) {
    this.main = main;
    if (main != null) {
      projectManager = main.getProjectManager();
      setLayout(new BorderLayout());
      scroll = new JScrollPane();
      add(scroll, BorderLayout.CENTER);
      DocumentTreeCellIcons icons = new DocumentTreeCellIcons(iconMap);
      LookAndFeelManager laf = BaseApplicationFrame.getLookAndFeelManager();
      if (laf.isSubstanceLnF()) { 
	treeCellRenderer = new SubstanceTreeCellRenderer(icons);
      } else { 
	treeCellRenderer = new DocumentTreeCellRenderer(icons);
      }
      buildProjectTree();
    }
  }

  public void setProjectManager(ProjectManager projectManager) {
    this.projectManager = projectManager;
    refreshTree();
  }

  public ProjectManager getProjectManager() { 
    return this.projectManager;
  }

  protected void buildProjectTree() {
    root = new ProjectTreeNode();
    tree = new JTree(root);
    ToolTipManager.sharedInstance().registerComponent(tree);
    tree.setCellRenderer(treeCellRenderer);
    tree.setShowsRootHandles(true);
    treeListener = new TreeListener();
    tree.addMouseListener(treeListener);
    tree.addMouseMotionListener(treeListener);
    model = (DefaultTreeModel) tree.getModel();
    scroll.setViewportView(tree);
    refreshTree();
  }

  protected void removeAllTreeNodes(DefaultMutableTreeNode parent) {
    if (parent != null) {
      int n = parent.getChildCount();
      for (int i = n - 1; i >= 0; i--) {
	TreeNode tn = parent.getChildAt(i);
	if (tn instanceof DrawShapeTreeNode) {
	  DrawShapeTreeNode dtn = (DrawShapeTreeNode) tn;
	  model.removeNodeFromParent(dtn);
	  if (!model.isLeaf(dtn)) {
	    removeAllTreeNodes(dtn);
	  }
	}
      }
    }
  }

  protected void refreshTree() {
    removeAllTreeNodes(root);

    if (projectManager != null) {
      int i = 0;
      Iterator<Project> iter = projectManager.allProjects();
      while (iter.hasNext()) {
	Project proj = iter.next();
	if (proj != null) {
	  ProjectTreeNode node = buildTreeNode(root, proj, i++);
	  if (projectManager.isDefaultProject(proj)) {
	    defaultProject = node;
	  }
	}
      }

      TreeNode[] nodes = root.getPath();
      TreePath path = new TreePath(nodes);
      tree.expandPath(path);
    }

    validate();
  }

  protected ProjectTreeNode buildTreeNode(DefaultMutableTreeNode parent,
					  ProjectItem item) {
    return buildTreeNode(parent, item, -1);
  }

  protected ProjectTreeNode buildTreeNode(DefaultMutableTreeNode parent,
					  ProjectItem item, int pos) {
    ProjectTreeNode node = null;
    if (parent != null && item != null) {
      node = new ProjectTreeNode(item);
      if (pos < 0) {
	pos = parent.getChildCount();
      }
      model.insertNodeInto(node, parent, pos);

      if (item instanceof ProjectFolder) {
	ProjectFolder folder = (ProjectFolder) item;
	int i = 0;
	Iterator<ProjectItem> iter = folder.allItems();
	while (iter.hasNext()) {
	  ProjectItem it = iter.next();
	  if (it != null) {
	    buildTreeNode(node, it, i++);
	  }
	}
      }
    }
    return node;
  }

  protected ProjectTreeNode findTreeNode(ProjectItem item) {
    return findTreeNode(root, item);
  }

  protected ProjectTreeNode findTreeNode(DefaultMutableTreeNode parent,
      ProjectItem item) {
    if (parent != null && item != null) {
      int n = parent.getChildCount();
      for (int i = 0; i < n; i++) {
	TreeNode tn = parent.getChildAt(i);
	if (tn instanceof ProjectTreeNode) {
	  ProjectTreeNode ptn = (ProjectTreeNode) tn;
	  if (item == ptn.getProjectItem()) {
	    return ptn;
	  }
	  if (!ptn.isLeaf()) {
	    ProjectTreeNode cn = findTreeNode(ptn, item);
	    if (cn != null) {
	      return cn;
	    }
	  }
	}
      }
    }
    return null;
  }

  protected ProjectTreeNode findTreeNode(SimpleDrawDocument doc) {
    return findTreeNode(root, doc);
  }

  protected ProjectTreeNode findTreeNode(DefaultMutableTreeNode parent,
					 SimpleDrawDocument doc) {
    if (parent != null && doc != null) {
      int n = parent.getChildCount();
      for (int i = 0; i < n; i++) {
	TreeNode tn = parent.getChildAt(i);
	if (tn instanceof ProjectTreeNode) {
	  ProjectTreeNode ptn = (ProjectTreeNode) tn;
	  ProjectItem item = ptn.getProjectItem();
	  if (item != null && doc == item.getDocument()) {
	    return ptn;
	  }
	  if (!ptn.isLeaf()) {
	    ProjectTreeNode cn = findTreeNode(ptn, doc);
	    if (cn != null) {
	      return cn;
	    }
	  }
	}
      }
    }
    return null;
  }

  protected ProjectTreeNode findProjectTreeNode(Project project) {
    if (project != null) {
      int n = root.getChildCount();
      for (int i = 0; i < n; i++) {
	TreeNode tn = root.getChildAt(i);
	if (tn instanceof ProjectTreeNode) {
	  ProjectTreeNode ptn = (ProjectTreeNode) tn;
	  if (project == ptn.getProjectItem()) {
	    return ptn;
	  }
	}
      }
    }
    return null;
  }

  public void addProject(Project project) {
    if (project != null) {
      projectManager.addProject(project, false);

      ProjectTreeNode projnode = new ProjectTreeNode(project);
      int pos = root.getChildCount();
      model.insertNodeInto(projnode, root, pos);

      setTreeSelection(projnode); 
    }
  }

  public void loadProject(String path) {
    if (path != null) {
      try {
	Project project = new Project(main);
	project.loadProjectFromPath(path);
	addProject(project);
      } catch (IOException e) {
	System.err.println("Load project failed: " + path);
	System.err.println(e.getMessage());
      } catch (ProjectException e) {
	System.err.println("Load project failed: " + path);
	System.err.println(e.getMessage());
      } catch (DocumentException e) {
	System.err.println("Load project failed: " + path);
	System.err.println(e.getMessage());
      }
    }
  }

  public void removeProject(ProjectTreeNode projnode, Project project) {
    if (projnode != null && project != null) {
      projectManager.removeProject(project, false);
      removeProjectItemView(project);

      model.removeNodeFromParent(projnode);
      validate();
    }
  }

  protected void removeProjectItemView(ProjectItem item) {
    if (item != null) {
      if (item instanceof ProjectFolder) {
	ProjectFolder folder = (ProjectFolder) item;
	Iterator<ProjectItem> iter = folder.allItems();
	while (iter.hasNext()) {
	  removeProjectItemView(iter.next());
	}
      } else {
	SimpleDrawDocument doc = item.getDocument();
	int i = main.findViewTab(doc);
	if (i >= 0) {
	  main.removeViewTabAt(i);
	}
      }
    }
  }

  public void addProjectFolder(ProjectTreeNode parentNode, String name,
			       ProjectFolder.Type type) {
    if (parentNode != null && name != null) {
      ProjectFolder newFolder = new ProjectFolder(name, type);
      ProjectFolder parent = (ProjectFolder) parentNode.getProjectItem();
      projectManager.addProjectItem(newFolder, parent, false);

      ProjectTreeNode newFolderNode = new ProjectTreeNode(newFolder);
      int pos = parentNode.getChildCount();
      model.insertNodeInto(newFolderNode, parentNode, pos);

      setTreeSelection(newFolderNode);
    }
  }

  public void addProjectItem(ProjectTreeNode parentNode, ProjectItem item) {
    if (parentNode != null && item != null) {
      ProjectFolder parent = (ProjectFolder) parentNode.getProjectItem();
      projectManager.addProjectItem(item, parent, false);

      ProjectTreeNode newItemNode = new ProjectTreeNode(item);
      int pos = parentNode.getChildCount();
      model.insertNodeInto(newItemNode, parentNode, pos);

      setTreeSelection(newItemNode);
    }
  }

  public void removeProjectItem(ProjectTreeNode pnode) {
    if (pnode != null) {
      ProjectItem item = pnode.getProjectItem();

      projectManager.removeProjectItem(item, false);

      // update tree
      model.removeNodeFromParent(pnode);
      validate();

      removeProjectItemView(item);
      /*
       * // delete view tab if (item != null) { String name = item.getName();
       * int i = main.findViewTab(name); if (i >= 0) {
       * main.removeViewTabAt(i); } }
       */
    }
  }

  public void moveProjectItem(ProjectTreeNode moveNode, ProjectTreeNode node) {
    if (moveNode != null && 
	node != null) {
      // System.out.println("ProjectPane.moveProjectItem() @1");

      ProjectItem moveItem = moveNode.getProjectItem();
      ProjectItem nodeItem = node.getProjectItem();

      ProjectTreeNode parentNode = null;
      ProjectFolder parentFolder = null;
      int pos = 0;
      if (nodeItem instanceof ProjectFolder) {
	parentNode = node;
	parentFolder = (ProjectFolder) nodeItem;
	pos = parentNode.getChildCount();
      } else {
	TreeNode pnode = node.getParent();
	if (pnode instanceof ProjectTreeNode) {
	  parentNode = (ProjectTreeNode) pnode;
	  parentFolder = (ProjectFolder) parentNode.getProjectItem();
	  pos = model.getIndexOfChild(parentNode, node); // + 1;
	}
      }

      if (parentNode != null && parentFolder != null) {
	//System.out.println("ProjectPane.moveProjectItem() @2 parent=" + parentFolder.getName());

	if (parentFolder != moveItem.getParent()) { 
	  String name = moveItem.getName();
	  if (parentFolder.hasChildByName(name)) {
	    name = parentFolder.getNewItemName(name + "-");
	    moveItem.setName(name);
	    SimpleDrawDocument doc = moveItem.getDocument();
	    if (doc != null) { 
	      main.updateTabTitle(doc);
	    }
	  }

	  projectManager.removeProjectItem(moveItem, false);
	  projectManager.addProjectItem(moveItem, parentFolder, false);

	  int n = parentNode.getChildCount();
	  if (pos < 0) {
	    pos = 0;
	  } else if (pos >= n) {
	    if (n == 0) { 
	      pos = 0;
	    } else { 
	      pos = n - 1;
	    }
	  }
	  //System.out.println("ProjectPane.moveProjectItem() n=" + n + " pos=" + pos);
	  model.removeNodeFromParent(moveNode);
	  model.insertNodeInto(moveNode, parentNode, pos);

	  setTreeSelection(moveNode);
	}
      }
    }
  }

  public void copyProjectItem(ProjectTreeNode copyNode, ProjectTreeNode node) {
    if (copyNode != null && 
	node != null) {
      // System.out.println("ProjectPane.copyProjectItem() @1");

      ProjectItem copyItem = copyNode.getProjectItem();
      ProjectItem nodeItem = node.getProjectItem();

      ProjectTreeNode parentNode = null;
      ProjectFolder parentFolder = null;
      int pos = 0;
      if (nodeItem instanceof ProjectFolder) {
	parentNode = node;
	parentFolder = (ProjectFolder) nodeItem;
	pos = parentNode.getChildCount();
      } else {
	TreeNode pnode = node.getParent();
	if (pnode instanceof ProjectTreeNode) {
	  parentNode = (ProjectTreeNode) pnode;
	  parentFolder = (ProjectFolder) parentNode.getProjectItem();
	  pos = model.getIndexOfChild(parentNode, node); // + 1;
	}
      }

      if (parentNode != null && parentFolder != null) {
	//System.out.println("ProjectPane.copyProjectItem() @2 parent=" + parentFolder.getName());

	ProjectItem clonedItem = null;
	ProjectTreeNode clonedNode = null; 
	if (copyItem instanceof ProjectFolder) { 

	} else { 
	  // clone new item 
	  // clone new node 
	  clonedItem = copyItem.cloneProjectItem();
	  String name = copyItem.getName();
	  if (parentFolder.hasChildByName(name)) {
	    name = parentFolder.getNewItemName(name + "-");
	    clonedItem.setName(name);
	  }
	  projectManager.addProjectItem(clonedItem, parentFolder, false);
	  main.addDocument(clonedItem.getDocument());
	  clonedNode = new ProjectTreeNode(clonedItem);
	}

	int n = parentNode.getChildCount();
	if (pos < 0) {
	  pos = 0;
	} else if (pos >= n) {
	  if (n == 0) { 
	    pos = 0;
	  } else { 
	    pos = n - 1;
	  }
	}

	//System.out.println("ProjectPane.copyProjectItem() n=" + n + " pos=" + pos);

	model.insertNodeInto(clonedNode, parentNode, pos);
	setTreeSelection(clonedNode);
      }
    }
  }

  public void projectAdded(Project project) {
    if (project != null) {
      ProjectTreeNode n;
      n = buildTreeNode(root, project);
      if (n != null) {
	setTreeSelection(n);
      }
    }
  }

  public void projectRemoved(Project project) {
    ProjectTreeNode n = findProjectTreeNode(project);
    if (n != null) {
      removeProjectItemView(project);

      model.removeNodeFromParent(n);
      validate();
    }
  }

  public void projectSelected(Project project) {
    ProjectTreeNode n = findProjectTreeNode(project);
    setTreeSelection(n);
  }

  public void projectItemAdded(ProjectItem item, int pos) {
    //System.out.println("ProjectPane.projectItemAdded()");
    if (item != null) {
      ProjectFolder parent = item.getParent();
      ProjectTreeNode n;
      if (parent != null) {
	ProjectTreeNode pnode = findTreeNode(parent);
	n = buildTreeNode(pnode, item, pos);
      } else {
	n = buildTreeNode(defaultProject, item, pos);
      }
      if (n != null) {
	// do not update bcb, fix a problem in bcb
	setTreeSelection(n, false);
      }
    }
  }

  public void projectItemRemoved(ProjectItem item) {
    ProjectTreeNode n = findTreeNode(item);
    if (n != null) {
      model.removeNodeFromParent(n);
      validate();
    }
  }

  public void projectItemSelected(ProjectItem item) {
    //System.out.println("ProjectPane.projectItemSelected()");
    ProjectTreeNode n = findTreeNode(item);
    setTreeSelection(n);
  }

  public void projectItemRenamed(ProjectItem item) {
    ProjectTreeNode n = findTreeNode(item);
    if (n != null) {
      model.nodeChanged(n);
    }
  }

  public void documentRenamed(SimpleDrawDocument doc) {
    ProjectTreeNode n = findTreeNode(doc);
    if (n != null) {
      model.nodeChanged(n);
    }
  }

  public void documentSelected(SimpleDrawDocument doc) {
    //System.out.println("ProjectPane.documentSelected()");
    ProjectTreeNode n = findTreeNode(doc);
    setTreeSelection(n, true); // update bcb

    ProjectItem item = projectManager.findItem(doc);
    projectManager.setCurrentItem(item);
    if (item != null) {
      projectManager.setCurrentProject(item.getOwner());
    }
  }

  public void clear() {
    removeAllTreeNodes(root);
    validate();
  }

  public void refresh() {
    refreshTree();
  }

  public JTree getProjectTree() { 
    return tree;
  }

  public BreadcrumbAdapter getBreadcrumbAdapter() { 
    return breadcrumbAdapter; 
  }

  public void setBreadcrumbAdapter(BreadcrumbAdapter breadcrumbAdapter) { 
    this.breadcrumbAdapter = breadcrumbAdapter; 
  }

  // called from breadcrumb bar for selection changes
  public void selectProjectTreeNode(ProjectTreeNode pnode) { 
    if (pnode != null) { 
      ProjectItem item = pnode.getProjectItem();
      if (item != null) {
	projectManager.setCurrentItem(item);
	projectManager.setCurrentProject(item.getOwner());
	if (item instanceof Project) {
	  // not updating bcb 
	  selectProjectFolder((ProjectFolder) item, false); 
	} else if (item instanceof ProjectFolder) {
	  // not updating bcb 
	  selectProjectFolder((ProjectFolder) item, false); 
	} else { 
	  // not updating bcb 
	  selectProjectItem(item, true, false);
	}
      }
    }
  }

  protected void setTreeSelection(ProjectTreeNode node) { 
    setTreeSelection(node, false); //true);
  }

  protected void setTreeSelection(ProjectTreeNode node, boolean updatebcb) { 
    if (node != null) { 
      TreeNode[] nodes = node.getPath();
      TreePath path = new TreePath(nodes);
      tree.setSelectionPath(path);
    } else { 
      // unselect the current node
      tree.setSelectionPath(null);
    }
    validate();

    if (updatebcb && breadcrumbAdapter != null) { 
      breadcrumbAdapter.updatePath();
    }
  }

  protected void selectProjectItem(ProjectItem item, boolean open, boolean updatebcb) {
    if (item != null) { 
      if (open && !item.isOpen()) { 
	try { 
	  item.openDocument();
	} catch (IOException ex) { 
	  main.reportOpenFileError(item.getName(), ex.getMessage());
	} catch (DocumentException ex) {
	  main.reportOpenFileError(item.getName(), ex.getMessage());
	}	  
      }
      Document doc = item.getDocument();
      if (doc instanceof SimpleDrawDocument) {
	main.showViewTab((SimpleDrawDocument) doc); // will indirectly update bcd
      }

      //if (updatebcb && breadcrumbAdapter != null) { 
      //breadcrumbAdapter.updatePath();
      //}
    }
  }

  protected void selectProjectFolder(ProjectFolder item, boolean updatebcb) { 
    main.setFrameTitle();
    if (updatebcb && 
	breadcrumbAdapter != null) { 
      breadcrumbAdapter.updatePath();
    }
  }

  protected DrawApp main;

  protected ProjectManager projectManager;

  protected JTree tree;

  protected DefaultMutableTreeNode root;

  protected TreeCellRenderer treeCellRenderer; 

  protected DefaultMutableTreeNode defaultProject;

  protected DefaultTreeModel model;

  protected JScrollPane scroll;

  protected TreeListener treeListener;

  protected TreeNode curNode = null;

  protected BreadcrumbAdapter breadcrumbAdapter; 

  class TreeListener 
    extends MouseAdapter 
    implements MouseListener, MouseMotionListener {

    protected int selRow;

    protected TreePath selPath;

    TreeListener() {
    }

    @Override
    public void mousePressed(MouseEvent e) {
      // moveNode = null;
      // System.out.println("ProjectPane #projects=" +
      // projectManager.getNumOfProjects());

      int x = e.getX();
      int y = e.getY();
      int button = e.getButton();
      int count = e.getClickCount();
      selRow = tree.getRowForLocation(x, y);
      int modifiers = e.getModifiers();
      if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
	// multiple selection
	TreePath[] paths = tree.getSelectionPaths();
	if (paths != null && paths.length > 0) {
	  for (int i = 0; i < paths.length; i++) {
	    ProjectTreeNode pnode = (ProjectTreeNode) paths[i].getLastPathComponent();
	    ProjectItem item = pnode.getProjectItem();

	  }
	}
      } else {
	// single selection
	selPath = tree.getPathForLocation(x, y);
	if (selRow != -1) {
	  DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
	  int pathLen = selPath.getPathCount();
	  if (pathLen > 0) {
	    curNode = (TreeNode) selPath.getLastPathComponent();
	    if (curNode instanceof ProjectTreeNode) {
	      ProjectTreeNode pnode = (ProjectTreeNode) curNode;
	      ProjectItem item = pnode.getProjectItem();
	      if (item != null) {
		projectManager.setCurrentItem(item);
		projectManager.setCurrentProject(item.getOwner());
	      }
	      
	      if (e.isPopupTrigger() || button == MouseEvent.BUTTON3) {
		// right mouse button
		JPopupMenu popup = makePopupMenu(pnode);
		if (popup != null) {
		  popup.show(tree, x, y);
		}
	      } else {
		if (item instanceof Project) {
		  selectProjectFolder((ProjectFolder) item, true); 
		} else if (item instanceof ProjectFolder) {
		  selectProjectFolder((ProjectFolder) item, true); 
		} else if (item != null) {
		  // project item, open if double clicked 
		  selectProjectItem(item, (count > 1), true);
		}
	      }
	    } else {

	    }
	  }
	}
      }
    }

    JPopupMenu movePopup = null;
    JMenuItem moveHereMenuItem = null;
    MoveItemHereAction moveHereAction = null;

    JPopupMenu copyPopup = null;
    JMenuItem copyHereMenuItem = null;
    CopyItemHereAction copyHereAction = null;

    @Override
    public void mouseDragged(MouseEvent e) {
      mouseMoved(e);
    }

    @Override
    public void mouseMoved(MouseEvent e) {
      if (moveNode != null ||
	  copyNode != null) {
	int x = e.getX();
	int y = e.getY();
	selRow = tree.getRowForLocation(x, y);
	if (selRow != -1) {
	  selPath = tree.getPathForLocation(x, y);
	  int pathLen = selPath.getPathCount();
	  if (pathLen > 0) {
	    curNode = (TreeNode) selPath.getLastPathComponent();
	    if (curNode instanceof ProjectTreeNode) {
	      ProjectTreeNode pnode = (ProjectTreeNode) curNode;
	      if (moveNode != null) { 
		if (movePopup == null) {
		  makeMovePopupMenu();
		}
		moveHereAction.node = pnode;
		movePopup.show(tree, x, y);
	      } else { 
		if (copyPopup == null) {
		  makeCopyPopupMenu();
		}
		copyHereAction.node = pnode;
		copyPopup.show(tree, x, y);
	      }
	    }
	  }
	}
      }
    }

    void makeMovePopupMenu() {
      movePopup = new JPopupMenu();
      moveHereMenuItem = new JMenuItem(getResourceString("MoveHere.text"));
      moveHereAction = new MoveItemHereAction();
      moveHereMenuItem.addActionListener(moveHereAction);
      movePopup.add(moveHereMenuItem);
    }

    void makeCopyPopupMenu() {
      copyPopup = new JPopupMenu();
      copyHereMenuItem = new JMenuItem(getResourceString("CopyHere.text"));
      copyHereAction = new CopyItemHereAction();
      copyHereMenuItem.addActionListener(copyHereAction);
      copyPopup.add(copyHereMenuItem);
    }

    JPopupMenu makePopupMenu(ProjectTreeNode node) {
      ProjectItem item = node.getProjectItem();
      JPopupMenu popup = new JPopupMenu();
      JMenuItem mi;
      ActionListener projPropAction = DrawApp.getAction("ProjectProperties");
      ActionListener projRenameAction = DrawApp.getAction("ProjectRename");
      ActionListener loadProjAction = DrawApp.getAction("LoadProject");
      ActionListener saveProjAction = DrawApp.getAction("SaveProject");
      
      if (item != null) {
	String name = item.getName();
	if (item instanceof Project) {
	  Project project = (Project) item;

	  popup.add(mi = new JMenuItem(getResourceString("NewProject.text")));
	  mi.addActionListener(projPropAction);
	  mi.setActionCommand("NewProject");

	  popup.add(mi = new JMenuItem(getResourceString("OpenProject.text")));
	  mi.addActionListener(loadProjAction);

	  popup.add(new JMenuItem(getResourceString("SaveProject.text") + " " + name));
	  mi.addActionListener(saveProjAction);

	  // popup.add(new JSeparator());
	  popup.add(mi = new JMenuItem(getResourceString("RenameProject.text") + " " + name));
	  mi.addActionListener(projRenameAction);
	  popup.add(mi = new JMenuItem(getResourceString("EditProject.text") + " " + name));
	  mi.addActionListener(projPropAction);

	  if (!projectManager.isDefaultProject(project)) {
	    popup.add(mi = new JMenuItem(getResourceString("CloseProject.text") + " " + name));
	    CloseProjectAction closeProjAction = new CloseProjectAction();
	    closeProjAction.project = project;
	    closeProjAction.node = node;
	    mi.addActionListener(closeProjAction);

	    // popup.add(new JSeparator());
	  }

	  popup.add(new JSeparator());

	  // popup.add(mi = new JMenuItem("Add subfolder in " +
	  // name));
	  NewFolderAction newFolderAction = new NewFolderAction();
	  newFolderAction.parent = node;
	  // mi.addActionListener(newFolderAction);
	  popup.add(makeNewFolderMenu(getResourceString("AddSubfolder.text") + " " + name, newFolderAction));

	  NewItemAction newItemAction = new NewItemAction();
	  newItemAction.parent = node;
	  popup.add(makeNewItemMenu(getResourceString("AddNewItem.text") + " " + name, newItemAction));

	} else if (item instanceof ProjectFolder) {
	  popup.add(mi = new JMenuItem(getResourceString("MoveFolder.text") + " " + name));
	  MoveItemAction moveItemAction = new MoveItemAction();
	  moveItemAction.node = node;
	  mi.addActionListener(moveItemAction);

	  popup.add(mi = new JMenuItem(getResourceString("DeleteFolder.text") + " " + name));
	  DeleteItemAction delItemAction = new DeleteItemAction();
	  delItemAction.node = node;
	  mi.addActionListener(delItemAction);

	  popup.add(mi = new JMenuItem(getResourceString("RenameFolder.text") + " " + name));
	  ProjectFolderPropertiesListener itemPropAction = new ProjectFolderPropertiesListener();
	  itemPropAction.setMain(main);
	  itemPropAction.setProjectFolder((ProjectFolder) item);
	  mi.addActionListener(itemPropAction);

	  popup.add(new JSeparator());

	  // popup.add(mi = new JMenuItem("Add subfolder in " +
	  // name));
	  NewFolderAction newFolderAction = new NewFolderAction();
	  newFolderAction.parent = node;
	  // mi.addActionListener(newFolderAction);
	  popup.add(makeNewFolderMenu(getResourceString("AddSubfolder.text") + " " + name, newFolderAction));

	  NewItemAction newItemAction = new NewItemAction();
	  newItemAction.parent = node;
	  popup.add(makeNewItemMenu(getResourceString("AddNewItem.text") + " " + name, newItemAction));
	} else {
	  // project item

	  popup.add(mi = new JMenuItem(getResourceString("MoveItem.text") + " " + name));
	  MoveItemAction moveItemAction = new MoveItemAction();
	  moveItemAction.node = node;
	  mi.addActionListener(moveItemAction);

	  popup.add(mi = new JMenuItem(getResourceString("CopyItem.text") + " " + name));
	  CopyItemAction copyItemAction = new CopyItemAction();
	  copyItemAction.node = node;
	  mi.addActionListener(copyItemAction);

	  popup.add(mi = new JMenuItem(getResourceString("DeleteItem.text") + " " + name));
	  DeleteItemAction delItemAction = new DeleteItemAction();
	  delItemAction.node = node;
	  mi.addActionListener(delItemAction);

	  popup.add(mi = new JMenuItem(getResourceString("RenameItem.text") + " " + name));
	  ProjectItemPropertiesListener itemPropAction = new ProjectItemPropertiesListener();
	  itemPropAction.setMain(main);
	  itemPropAction.setProjectItem(item);
	  mi.addActionListener(itemPropAction);

	  popup.add(mi = new JMenuItem(getResourceString("EditItem.text") + " " + name));
	  EditProjectItemListener editDocAction = new EditProjectItemListener();
	  editDocAction.setMain(main);
	  editDocAction.setProjectItem(item);
	  mi.addActionListener(editDocAction);

	  popup.add(new JSeparator());
	  if (item.isOpen()) { 
	    popup.add(mi = new JMenuItem(getResourceString("CloseItem.text") + " " + name));
	    CloseItemAction closeItemAction = new CloseItemAction();
	    closeItemAction.setProjectItem(item);
	    mi.addActionListener(closeItemAction);
	  } else { 
	    popup.add(mi = new JMenuItem(getResourceString("OpenItem.text") + " " + name));
	    OpenItemAction openItemAction = new OpenItemAction();
	    openItemAction.setProjectItem(item);
	    mi.addActionListener(openItemAction);
	  }

	  ProjectFolder parent = item.getParent();
	  ProjectTreeNode parentNode = (ProjectTreeNode) node.getParent();
	  if (parent != null && parentNode != null) {
	    String pname = parent.getName();
	    popup.add(new JSeparator());

	    // popup.add(mi = new JMenuItem("Add new folder in " + pname));
	    NewFolderAction newFolderAction = new NewFolderAction();
	    newFolderAction.parent = parentNode;
	    // mi.addActionListener(newFolderAction);
	    popup.add(makeNewFolderMenu(getResourceString("AddNewFolder.text") + " " + pname, newFolderAction));

	    // popup.add(mi = new JMenuItem("Add new item in " + pname));
	    NewItemAction newItemAction = new NewItemAction();
	    newItemAction.parent = parentNode;
	    // mi.addActionListener(newItemAction);
	    popup.add(makeNewItemMenu(getResourceString("AddNewItem.text") + " " + pname, newItemAction));
	  }
	}
      } else {
	popup.add(mi = new JMenuItem(getResourceString("NewProject.text")));
	mi.addActionListener(projPropAction);
	mi.setActionCommand("NewProject");

	popup.add(mi = new JMenuItem(getResourceString("OpenProject.text")));
	// LoadProjectAction loadProjAction = new LoadProjectAction();
	mi.addActionListener(loadProjAction);

	/*
	popup.add(new JSeparator());
	popup.add(mi = new JMenuItem(getResourceString("CloseAllProjects.text")));
	CloseAllProjectsAction closeAllProjAction = new CloseAllProjectsAction();
	mi.addActionListener(closeAllProjAction);
	*/
	// popup.add(mi = new JMenuItem("Save all projects"));
      }
      return popup;
    }

    JMenu makeNewItemMenu(String title, ActionListener action) {
      JMenu menu = new JMenu(title);
      JMenuItem mi;

      for (int i = 0; i < Document.DocumentTypeNames.length; i++) {
	String docname = getResourceString("DiagramType." + Document.DocumentTypeNames[i] + ".text");
	mi = new JMenuItem(docname);
	mi.setActionCommand(Document.DocumentTypeNames[i]);
	mi.addActionListener(action);
	menu.add(mi);
      }
      return menu;
    }

    JMenu makeNewFolderMenu(String title, ActionListener action) {
      JMenu menu = new JMenu(title);
      JMenuItem mi;

      for (int i = 0; i < Document.FolderTypeNames.length; i++) {
	String folderType = 
	  getResourceString("FolderType." + Document.FolderTypeNames[i] + ".text");
	mi = new JMenuItem(folderType);
	mi.setActionCommand(Document.FolderTypeNames[i]);
	mi.addActionListener(action);
	menu.add(mi);
      }
      return menu;
    }

  }

  class CloseProjectAction implements ActionListener {

    ProjectTreeNode node;

    Project project;

    public void actionPerformed(ActionEvent evt) {
      if (node != null && project != null) {
	if (!projectManager.isDefaultProject(project)) {
	  String name = project.getName();
	  try {
	    projectManager.saveProject(project);
	    removeProject(node, project);
	    main.notifyTabClosed();
	  } catch (IOException e) {
	    String msg1 = "Save project error: " + name;
	    String msg2 = e.getMessage();
	    main.displayMessage(msg1);
	    JOptionPane.showMessageDialog(main.getJFrame(), 
					  msg1 + "\n" + msg2,
					  "Save Project Error", 
					  JOptionPane.ERROR_MESSAGE);
	  } catch (DocumentException e) {
	    String msg1 = "Save project error: " + name;
	    String msg2 = e.getMessage();
	    main.displayMessage(msg1);
	    JOptionPane.showMessageDialog(main.getJFrame(),
					  msg1 + "\n" + msg2,
					  "Save Project Error", 
					  JOptionPane.ERROR_MESSAGE);
	  }
	}
      }
    }

  }

  class CloseAllProjectsAction implements ActionListener {

    public void actionPerformed(ActionEvent evt) {
      int n = root.getChildCount();
      for (int i = 0; i < n; i++) {
	TreeNode tn = root.getChildAt(i);
	if (tn instanceof ProjectTreeNode) {
	  ProjectTreeNode ptn = (ProjectTreeNode) tn;
	  Project project = (Project) ptn.getProjectItem();
	  if (!projectManager.isDefaultProject(project)) {
	    String name = project.getName();
	    try {
	      projectManager.saveProject(project);
	      projectManager.removeProject(project, false);
	      model.removeNodeFromParent(ptn);
	    } catch (IOException e) {
	      String msg1 = "Save project error: " + name;
	      String msg2 = e.getMessage();
	      main.displayMessage(msg1);
	      JOptionPane.showMessageDialog(main.getJFrame(), 
					    msg1 + "\n" + msg2,
					    "Save Project Error", 
					    JOptionPane.ERROR_MESSAGE);
	    } catch (DocumentException e) {
	      String msg1 = "Save project error: " + name;
	      String msg2 = e.getMessage();
	      main.displayMessage(msg1);
	      JOptionPane.showMessageDialog(main.getJFrame(),
					    msg1 + "\n" + msg2,
					    "Save Project Error", 
					    JOptionPane.ERROR_MESSAGE);
	    }
	  }
	}
      }
      validate();
    }

  }

  class NewFolderAction implements ActionListener {

    ProjectTreeNode parent;

    public void actionPerformed(ActionEvent e) {
      if (parent != null) {
	ProjectFolder.Type type = ProjectFolder.Type.Default;
	Object src = e.getSource();
	if (src instanceof JMenuItem) {
	  String cmd = ((JMenuItem) src).getActionCommand();
	  if (cmd != null) {
	    type = ProjectFolder.Type.toFolderType(cmd);
	  }
	}
	ProjectItem item = parent.getProjectItem();
	if (item instanceof ProjectFolder) {
	  ProjectFolder parentFolder = (ProjectFolder) item;
	  String newname = "Folder";
	  switch (type) {
	  case UML_Model:
	    newname = "Model";
	    break;
	  case UML_Package:
	    newname = "Package";
	    break;
	  case Default:
	  case Source_Code:
	  case Documentation:
	  case Test:
	  case File_Folders:
	  default:
	  }
	  String newFolderName = parentFolder.getNewItemName(newname);
	  addProjectFolder(parent, newFolderName, type);
	}
      }
    }

  }

  ProjectTreeNode moveNode = null;

  class MoveItemAction implements ActionListener {

    ProjectTreeNode node;

    public void actionPerformed(ActionEvent e) {
      if (node != null) {
	moveNode = node;
      }
    }
  }

  class MoveItemHereAction implements ActionListener {

    ProjectTreeNode node;

    public void actionPerformed(ActionEvent e) {
      if (node != null && moveNode != null) {
	// System.out.println("MoveItemHereAction.actionPerformed()");

	moveProjectItem(moveNode, node);
	moveNode = null;
      }
    }
  }

  ProjectTreeNode copyNode = null;

  class CopyItemAction implements ActionListener {

    ProjectTreeNode node;

    public void actionPerformed(ActionEvent e) {
      if (node != null) {
	copyNode = node;
      }
    }
  }

  class CopyItemHereAction implements ActionListener {

    ProjectTreeNode node;

    public void actionPerformed(ActionEvent e) {
      if (node != null && copyNode != null) {
	// System.out.println("CopyItemHereAction.actionPerformed()");

	copyProjectItem(copyNode, node);
	copyNode = null;
      }
    }
  }

  class DeleteItemAction implements ActionListener {

    ProjectTreeNode node;

    public void actionPerformed(ActionEvent e) {
      if (node != null) {
	ProjectItem item = node.getProjectItem();
	Project project = item.getOwner();

	String oldfilename = item.getDocumentFullPath();
	String msg;
	if (item instanceof ProjectFolder) { 
	  msg = "DeleteFolder";
	} else { 
	  msg = "DeleteItem";
	}
	if (main.deleteProjectItemDialog(msg, item.getName())) { 
	  removeProjectItem(node);
	  File oldfile = new File(oldfilename);
	  oldfile.delete();
	  
	  try { 
	    project.saveProject();
	  } catch (IOException ex) {
	    System.err.println(ex); 
	  } catch (DocumentException ex) { 
	    System.err.println(ex);
	  }
	  main.notifyTabClosed();
	}
      }
    }
  }

  class NewItemAction implements ActionListener {

    ProjectTreeNode parent;

    public void actionPerformed(ActionEvent e) {
      if (parent != null) {
	Document.DocumentType type = Document.DocumentType.Generic_Diagram;
	Object src = e.getSource();
	if (src instanceof JMenuItem) {
	  String cmd = ((JMenuItem) src).getActionCommand();
	  if (cmd != null) {
	    if ("Default".equals(cmd)) {
	      type = Document.DocumentType.Generic_Diagram;
	    } else if ("UML".equals(cmd)) {
	      type = Document.DocumentType.UML_Diagram;
	    } else {
	      type = Document.toDocumentType(cmd);
	    }
	  }
	}
	ProjectFolder parentFolder = (ProjectFolder) parent.getProjectItem();
	main.newProjectItem(parentFolder, type);
      }
    }

  }

  class OpenItemAction implements ActionListener {
    
    public void actionPerformed(ActionEvent e) {
      if (!item.isOpen()) {
	try { 
	  item.loadDocument();
	} catch (IOException ex) { 
	  main.reportOpenFileError(item.getName(), ex.getMessage());
	} catch (DocumentException ex) {
	  main.reportOpenFileError(item.getName(), ex.getMessage());
	}
      }
    }
    
    public void setProjectItem(ProjectItem item) {
      this.item = item;
    }
    
    protected ProjectItem item;
  }

  class CloseItemAction implements ActionListener {
    
    public void actionPerformed(ActionEvent e) {
      if (item.isOpen()) {
	Document doc = item.getDocument();
	if (doc instanceof SimpleDrawDocument) {
	  main.closeTab((SimpleDrawDocument) doc);
	}
      }
    }
    
    public void setProjectItem(ProjectItem item) {
      this.item = item;
    }
    
    protected ProjectItem item;
  }

}
